﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Aletfa.LightManager.Board;

namespace Aletfa.LightManager
{
    /// <summary>
    /// Interaction logic for GridControl.xaml
    /// </summary>
    internal partial class GridControl : UserControl
    {
        public Cell SelectedCell { get; private set; }

        private Cell[,] Matrix { get; set; }

        public bool IsSetupMode { get; private set; }

        public event GenericEventHandler<Unit> SelectionChanged;

        public int CellSize { get; set; }

        public GridControl()
        {
            InitializeComponent();
            this.CellSize = 50;
        }
        
        public void Initialize(int columns, int rows)
        {
            this.ClearBoard();
            this.Matrix = new Cell[columns, rows];

            for (int c = 0; c < this.Matrix.GetLength(0); c++)
            {
                this.Grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(this.CellSize) });
                for (int r = 0; r < this.Matrix.GetLength(1); r++)
                {
                    this.Grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(this.CellSize) });
                    Rectangle rectangle = new Rectangle();
                    this.Matrix[c, r] = new Cell() { Graphic = rectangle };
                    rectangle.AllowDrop = true;
                    rectangle.MouseDown += rectangle_MouseDown;
                    rectangle.MouseMove += rectangle_MouseMove;
                    rectangle.MouseEnter += rectangle_MouseEnter;
                    rectangle.DragEnter += rectangle_DragEnter;
                    rectangle.DragLeave += rectangle_DragLeave;
                    rectangle.Drop += rectangle_Drop;
                    rectangle.MouseMove += rectangle_MouseMove;
                    rectangle.StrokeThickness = 1;
                    rectangle.Width = rectangle.Height = this.CellSize;
                    this.Render(rectangle);

                    this.Grid.Children.Insert(0, rectangle);
                    Grid.SetColumn(rectangle, c);
                    Grid.SetRow(rectangle, r);
                }
            }

            this.Preview.Height = this.Preview.Width = this.CellSize;
            this.Selection.Height = this.Selection.Width = this.CellSize;

            this.Grid.Children.Add(this.imgSettings);
            Grid.SetColumn(this.imgSettings, this.Matrix.GetLength(0));
            Grid.SetRow(this.imgSettings, 0);

            this.Grid.Children.Add(this.Preview);
            Grid.SetColumn(this.Preview, 0);
            Grid.SetRow(this.Preview, 0);

            this.Grid.Children.Add(this.Selection);
            Grid.SetColumn(this.Selection, 0);
            Grid.SetRow(this.Selection, 0);
        }

        public void SetupBoard(bool active)
        {
            this.IsSetupMode = active;
            this.SetupPanel.Visibility = (active) ? Visibility.Visible : Visibility.Collapsed;
            this.Selection.Visibility = (active) ? Visibility.Hidden : Visibility.Visible;
            this.imgSettings.Visibility = this.SetupPanel.Visibility;
            this.Select(null);
        }

        public void ClearBoard()
        {
            this.Grid.Children.Clear();
            this.Grid.ColumnDefinitions.Clear();
            this.Grid.RowDefinitions.Clear();

            this.Select(null);
        }


        public void RefreshUnits(ICollection<Unit> units)
        {
            List<Unit> active = new List<Unit>();
            foreach (Cell cell in this.Matrix)
                if (cell.Piece != null)
                    active.Add(cell.Piece.DataSource);

            foreach (Unit unit in active)
                if (!units.Contains(unit))
                    this.RemoveUnit(unit);

            foreach (Unit unit in units)
                if (!active.Contains(unit))
                    this.AddUnit(unit);
        }

        public void UpdateUnit(Unit unit)
        {
            Cell cell = this.GetCellByUnit(unit);
            if (cell == null) return;
            cell.Piece.DataSource = unit;
        }

        private void AddUnit(Unit unit)
        {
            //cerco la prima cella disponibile
            Cell cell = null;
            foreach (Cell c in this.Matrix)
                if (c.Piece == null)
                {
                    cell = c;
                    break;
                }

            UnitPieceControl piece = new UnitPieceControl();
            piece.DataSource = unit;
            cell.Piece = piece;
            this.Grid.Children.Add(piece);
            Grid.SetColumn(piece, cell.Location.Column);
            Grid.SetRow(piece, cell.Location.Row);
        }

        private void RemoveUnit(Unit unit)
        {
            Cell cell = this.GetCellByUnit(unit);
            if (cell == null) return;
            this.Grid.Children.Remove(cell.Piece);
            cell.Piece = null;
        }

        private UnitPieceControl GetPieceByUnit(Unit unit)
        {
            foreach (Cell cell in this.Matrix)
                if (cell.Piece != null)
                    if (cell.Piece.DataSource.Name == unit.Name)
                        return cell.Piece;
            return null;
        }

        private Cell GetCellByUnit(Unit unit)
        {
            foreach (Cell cell in this.Matrix)
                if (cell.Piece != null)
                    if (cell.Piece.DataSource.Name == unit.Name)
                        return cell;
            return null;
        }

        private void MoveItems(Cell from, Cell to)
        {
            Grid.SetColumn(from.Piece, to.Location.Column);
            Grid.SetRow(from.Piece, to.Location.Row);
            to.Piece = from.Piece;
            from.Piece = null;
        }

        private void Render(Rectangle square)
        {
            Cell cell = this.GetCell(square);
            square.Fill = (cell.Available) ? Brushes.Transparent : new SolidColorBrush(Color.FromArgb(255, 100, 100, 100));
            square.Stroke = (cell.Available) ? Brushes.LightGray : square.Fill;
            square.StrokeThickness = 1;
        }

        private Cell GetCell(Rectangle square)
        {
            return (square != null) ? this.Matrix[Grid.GetColumn(square), Grid.GetRow(square)] : null;
        }

        private void Select(Rectangle square)
        {
            if (this.SelectedCell == this.GetCell(square)) return;

            //fermo l'animazione
            if (this.SelectedCell != null)
                if (this.SelectedCell.Piece != null)
                    this.SelectedCell.Piece.SelectionAnimation(false);

            this.SelectedCell = (square != null) ? this.GetCell(square) : null;
            this.Selection.Visibility = (square != null) ? Visibility.Visible : Visibility.Collapsed;
            if (square == null) return;

            Grid.SetColumn(this.Selection, this.SelectedCell.Location.Column);
            Grid.SetRow(this.Selection, this.SelectedCell.Location.Row);

            if (this.SelectedCell.Piece != null)
                this.SelectedCell.Piece.SelectionAnimation(true);

            if (this.SelectionChanged != null)
                this.SelectionChanged((this.SelectedCell.Piece != null) ? this.SelectedCell.Piece.DataSource : null);
        }

        void grid_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (this.Matrix == null) return;
            this.SetupPanel.Width = this.Grid.ActualWidth;
            this.SetupPanel.Height = this.Matrix.GetLength(1) * this.CellSize;
        }

        void grid_MouseEnter(object sender, MouseEventArgs e)
        {
            this.Preview.Visibility = Visibility.Visible;
        }

        void grid_MouseLeave(object sender, MouseEventArgs e)
        {
            this.Preview.Visibility = Visibility.Collapsed;
        }

        void rectangle_Drop(object sender, DragEventArgs e)
        {
            if (this.IsSetupMode) return;
            Rectangle current = sender as Rectangle;
            if (e.Data.GetDataPresent(typeof(BoardPointer)))
            {
                this.Render(current);
                BoardPointer pointer = (BoardPointer)e.Data.GetData(typeof(BoardPointer));
                Cell sourceCell = this.Matrix[pointer.Column, pointer.Row];
                Cell currentCell = this.GetCell(current);
                if (currentCell.Piece != null) return;
                if (sourceCell.Graphic == current) return;
                this.MoveItems(sourceCell, currentCell);
                this.Select(current);
            }
        }

        void rectangle_DragEnter(object sender, DragEventArgs e)
        {
            if (this.IsSetupMode) return;
            if (e.Data.GetDataPresent(typeof(BoardPointer)))
            {
                Rectangle current = (Rectangle)sender;
                current.Stroke = Brushes.Gray;
                Cell cell = this.GetCell((Rectangle)sender);
                if (cell.Piece != null)
                {
                    current.Stroke = Brushes.Red;
                    current.StrokeThickness = 2;
                }
                else
                    current.Fill = GraphicUtilities.BrushFromHtml("#D0F5A9");
            }
        }

        void rectangle_DragLeave(object sender, DragEventArgs e)
        {
            if (this.IsSetupMode) return;
            this.Render((Rectangle)sender);
        }

        void rectangle_MouseMove(object sender, MouseEventArgs e)
        {
            Rectangle current = sender as Rectangle;
            Grid.SetColumn(this.Preview, Grid.GetColumn(current));
            Grid.SetRow(this.Preview, Grid.GetRow(current));
            if (!this.IsSetupMode)
                if (e.LeftButton == MouseButtonState.Pressed)
                    if (this.GetCell(current).Piece != null)
                        DragDrop.DoDragDrop(current, new BoardPointer() { Column = Grid.GetColumn(current), Row = Grid.GetRow(current) }, DragDropEffects.Move);
        }

        void rectangle_MouseEnter(object sender, MouseEventArgs e)
        {
            if (this.IsSetupMode)
                if (e.RightButton == MouseButtonState.Pressed)
                {
                    Rectangle current = sender as Rectangle;
                    Cell cell = this.GetCell(current);
                    cell.Available = false;
                    this.Render(current);
                }
        }

        void rectangle_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Rectangle current = sender as Rectangle;
            if (this.IsSetupMode)
            {
                Cell cell = this.GetCell(current);
                if (e.LeftButton == MouseButtonState.Pressed)
                    cell.Available = true;

                if (e.RightButton == MouseButtonState.Pressed)
                    cell.Available = false;

                this.Render(current);
                return;
            }

            this.Select((e.LeftButton == MouseButtonState.Pressed) ? current : null);
        }

        private void imgSettings_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.txtColumns.Text = this.Matrix.GetLength(0).ToString();
            this.txtRows.Text = this.Matrix.GetLength(1).ToString();
            this.pnlSettings.Visibility = System.Windows.Visibility.Visible;
            this.SetupPanel.Background = new SolidColorBrush(Color.FromArgb(100, 0, 0, 0));
        }

        private void btnCloseSettings_Click(object sender, RoutedEventArgs e)
        {
            this.pnlSettings.Visibility = System.Windows.Visibility.Hidden;
            this.SetupPanel.Background = null;
            this.Grid.IsEnabled = true;
            this.Initialize(Convert.ToInt32(this.txtColumns.Text), Convert.ToInt32(this.txtRows.Text));
        }

        public void ShowHealAnimation(Unit unit, int ammount)
        {
            this.GetPieceByUnit(unit).HealAnimation(true, ammount);
        }

        public void ShowDamageAnimation(Unit unit, int ammount)
        {
            this.GetPieceByUnit(unit).LoseLifeAnimation(true, ammount);
        }
    }
}
